/*
 * RedisConnectPool.cpp
 *
 *  Created on: 2018年1月29日
 *      Author: cxw
 */

#include "RedisConnectPool.h"
#include <algorithm>

CRedisConnectPool* CRedisConnectPool::m_pInstance = new CRedisConnectPool();
CRedisConnectPool* CRedisConnectPool::Instance()
{
	return m_pInstance;
}

CRedisConnectPool::CRedisConnectPool() :
		m_strIp("127.0.0.1"),
		m_iPort(6379),
		m_iTimeOut(10),
		m_iInitConNum(20),
		m_iMaxConNum(5000),
		m_iMinConNum(10),
		m_iActiveConNum(20),
		m_pMutex(NULL),
		m_bOpen(false)
{
	m_pMutex = new pthread_mutex_t;
	pthread_mutex_init(m_pMutex, NULL);
}

CRedisConnectPool::~CRedisConnectPool()
{
	Close();
	if(NULL == m_pInstance)
	{
		delete m_pInstance;
		m_pInstance = NULL;
	}
	pthread_mutex_destroy(m_pMutex);
	if(NULL == m_pMutex)
	{
		delete m_pMutex;
		m_pMutex = NULL;
	}
}

bool CRedisConnectPool::Open()
{
	//初始化所有Redis连接
	for(int i = 0; i < m_iInitConNum; i ++)
	{
		CRedisConnectPool::ST_Redis stRedis;
		stRedis.pRedis = new CRedisManager();
		stRedis.pRedis->SetIpAndPort(m_strIp, m_iPort);
		if(!stRedis.pRedis->Connect())
			return false;
		stRedis.redisStatus = EM_REDIS_FREE;
		time_t timeCon;
		time(&timeCon);
		stRedis.time = timeCon;
		m_vstRedis.push_back(stRedis);
	}
	m_iActiveConNum = m_iInitConNum;

	//初始化信号量
	sem_init(&m_sem, 0, m_iMaxConNum);

	m_bOpen = true;
	return true;
}

void CRedisConnectPool::Close()
{
	for(size_t i = 0; i < m_vstRedis.size(); i ++)
	{
		m_vstRedis.at(i).pRedis->Close();
		delete m_vstRedis.at(i).pRedis;
		m_vstRedis.at(i).redisStatus = EM_REDIS_FREE;
	}
	vector<ST_Redis>().swap(m_vstRedis);

	sem_destroy(&m_sem);
	m_bOpen = false;
}

CRedisManager* CRedisConnectPool::GetRedis()
{
	if(!m_bOpen)
		return NULL;
	cout << "redis connect Num : " <<m_vstRedis.size() << endl;
	sem_wait(&m_sem);

	CRedisManager* pRedis = NULL;

	pthread_mutex_lock(m_pMutex);
	DeleteTimeOutCon();
	if(m_iActiveConNum > 0)
	{
		int iId = -1;
		iId = FindRedisCon();
		if(iId != -1)
		{
			cout <<"use old Redis." <<endl;
			pRedis = m_vstRedis.at(iId).pRedis;
			m_vstRedis.at(iId).redisStatus = EM_REDIS_USE;
			m_iActiveConNum --;
		}
	}
	else if((int)m_vstRedis.size() < m_iMaxConNum)
	{
		cout <<"creat new Redis." <<endl;
		CRedisConnectPool::ST_Redis stRedis;
		stRedis.pRedis = new CRedisManager();
		stRedis.pRedis->SetIpAndPort(m_strIp, m_iPort);

		bool bCon = false;
		int iRecord = 0;
		while(!(bCon = stRedis.pRedis->Connect()))
		{
			if(iRecord == 10)
				break;
			iRecord ++;
		}
		if(bCon)
		{
			stRedis.redisStatus = EM_REDIS_USE;
			time_t timeCon;
			time(&timeCon);
			stRedis.time = timeCon;
			m_vstRedis.push_back(stRedis);
			pRedis = m_vstRedis.at(m_vstRedis.size() - 1).pRedis;
		}
	}

	pthread_mutex_unlock(m_pMutex);

	return pRedis;
}

void CRedisConnectPool::ReleaseRedis(CRedisManager* pRedis )
{
	if(!m_bOpen)
		return ;

	pthread_mutex_lock(m_pMutex);
	vector<ST_Redis>::iterator it = find(m_vstRedis.begin(), m_vstRedis.end(), pRedis);
	if(it != m_vstRedis.end())
	{
		it->redisStatus = EM_REDIS_FREE;
		m_iActiveConNum ++;
		sem_post(&m_sem);
	}
	pthread_mutex_unlock(m_pMutex);
}

void CRedisConnectPool::DeleteTimeOutCon()
{
	for(size_t i = 0; i < m_vstRedis.size(); )
	{
		if(m_vstRedis.at(i).redisStatus == EM_REDIS_FREE)
		{
			if(!m_vstRedis.at(i).pRedis->Ping())
			{
				if((int)m_vstRedis.size() < m_iMinConNum)
				{
					//连接已被关闭，如果连接数小于最小连接数， 重连
					bool bCon = false;
					int iRecord = 0;
					while(!(bCon = m_vstRedis.at(i).pRedis->Connect()))
					{
						if(iRecord == 10)
							break;
						iRecord ++;
					}
					i ++;
				}
				else
				{
					//连接已被关闭，清除vector
					m_vstRedis.at(i).pRedis->Close();
					delete m_vstRedis.at(i).pRedis;
					m_vstRedis.erase(m_vstRedis.begin() + i);
					m_iActiveConNum --;
				}
			}
			else
			{
				//连接未被关闭，判断是否超时
				time_t now;
				time(&now);
				if((int)m_vstRedis.size() < m_iMinConNum)
				{
					m_vstRedis.at(i).time = now;
					i ++;
				}
				else
				{
					double dConst = 0.0;
					dConst = difftime(now, m_vstRedis.at(i).time);
					if(dConst > m_iTimeOut)
					{
						m_vstRedis.at(i).pRedis->Close();
						delete m_vstRedis.at(i).pRedis;
						m_vstRedis.erase(m_vstRedis.begin() + i);
						m_iActiveConNum --;
					}
					else
						i ++;
				}
			}
		}
		else
			i ++;
	}
}

int CRedisConnectPool::FindRedisCon()
{
	int iRet = -1;
	for(size_t i = 0; i < m_vstRedis.size(); i ++)
	{
		if(m_vstRedis.at(i).redisStatus == EM_REDIS_FREE)
		{
			iRet = i;
			break;
		}
	}

	return iRet;
}
